package com.shulidata.mock.controller.ops;

import com.shulidata.mock.utils.DateExtUtils;
import com.shulidata.mock.utils.sec.RSACoderUtil;
import com.shulidata.mock.utils.sec.RandomStringUtil;
import org.apache.commons.lang3.StringUtils;

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 数立授权采集平台SDK.
 *
 * Created by jiangdawei on 16/7/4.
 */
public final class AuthCollURLBuilder {
    /**
     * 机构编码
     */
    private final String orgCode;
    /**
     * 机构私钥
     */
    private final String privateKey;
    /**
     * 采集平台公钥
     */
    private final String publicKey;

    public AuthCollURLBuilder(String orgCode, String privateKey, String publicKey) {
        this.orgCode = orgCode;
        this.privateKey = privateKey;
        this.publicKey = publicKey;
    }


    /***
     * 获取业务对象
     *
     * @param dataSource 数据源
     * @param extraData  json格式字符串,扩展参数{"CITY_CODE":"用于定位","ITEM_CODE":"用于跳转到默认的公积金登录页"}
     * @return
     */
    public String enc(String dataSource, String extraData) throws Exception {

        // 2. 商户私钥加签
        String saltData = getSaltData(); // 获取盐值

        String urlParams = buildParams(dataSource, getBizNo(), extraData); // 拼接参数

        urlParams = URLEncoder.encode(urlParams, AuthCheckConstant.CODE_SET);

        String signNameSource = getSignName(urlParams, saltData); // 获取签名

        String mixSalt = encryptByPrivateKey(saltData, this.privateKey); // 盐值加密

        urlParams = buildUrlParams(urlParams, signNameSource, mixSalt); // 拼接参数,签名,加密后的盐值

        // 3. 采集平台公钥加密
        urlParams = encryptByPublicKey(urlParams, this.publicKey);

        return urlParams;
    }

    /***
     * 获取签名
     *
     * @param text     明文
     * @param saltData 盐值
     * @return
     * @throws Exception
     */
    public String getSignName(String text, String saltData) throws Exception {
        try {
            return RSACoderUtil.encryptWithSHA(text + saltData, AuthCheckConstant.CODE_SET);
        } catch (Exception e) {
            throw new Exception("生成数据签名失败", e);
        }

    }

    /***
     * 私钥加密
     *
     * @param text       明文
     * @param privateKey 私钥
     * @return
     */
    public String encryptByPrivateKey(String text, String privateKey) throws Exception {
        try {
            return RSACoderUtil.encryptByPrivateKey(text, privateKey, AuthCheckConstant.KEY_ALGORITHM,
                    AuthCheckConstant.MAX_ENCRYPT_BLOCK);
        } catch (Exception e) {
            throw new Exception("私钥加密失败", e);
        }
    }

    /***
     * 公钥加密
     *
     * @param text      明文
     * @param publicKey 公钥
     * @return
     */
    public String encryptByPublicKey(String text, String publicKey) throws Exception {
        try {
            return RSACoderUtil.encryptByPublicKey(text, publicKey, AuthCheckConstant.KEY_ALGORITHM,
                    AuthCheckConstant.MAX_ENCRYPT_BLOCK);
        } catch (Exception e) {
            throw new Exception("公钥加密失败", e);
        }
    }

    /***
     * 私钥解密
     *
     * @param text       密文
     * @param privateKey 私钥
     * @return
     */
    public String decryptByPrivateKey(String text, String privateKey) throws Exception {
        try {
            return RSACoderUtil.decryptByPrivateKey(text, privateKey, AuthCheckConstant.KEY_ALGORITHM,
                    AuthCheckConstant.MAX_DECRYPT_BLOCK);
        } catch (Exception e) {
            throw new Exception("私钥解密失败", e);
        }
    }

    /***
     * 公钥解密
     *
     * @param text      密文
     * @param publicKey 公钥
     * @return
     * @throws Exception
     */
    public String decryptByPublicKey(String text, String publicKey) throws Exception {
        try {
            return RSACoderUtil.decryptByPublicKey(text, publicKey, AuthCheckConstant.KEY_ALGORITHM,
                    AuthCheckConstant.MAX_DECRYPT_BLOCK);
        } catch (Exception e) {
            throw new Exception("公钥解密失败", e);
        }
    }

    /***
     * 获取鉴权之后的明文结果
     *
     * @param text 密文
     * @return
     */
    public Map<String, String> getAuthResult(String text) throws Exception {
        //1. 商户私钥解密
        String decryptData = decryptByPrivateKey(text, this.privateKey);

        // 拆分数据
        String deSourceData = decryptData.split("&" + AuthCheckConstant.SIGN_NAME + "=")[0];
        String signData = decryptData.split("&" + AuthCheckConstant.SIGN_NAME + "=")[1];
        String signNameSource = signData.split("&" + AuthCheckConstant.MIX_SALT + "=")[0];
        String mixSalt = signData.split("&" + AuthCheckConstant.MIX_SALT + "=")[1];

        //2. 解析数据
        Map<String, String> result = splitStr(deSourceData);

        //2. 采集平台公钥验签
        String saltData = decryptByPublicKey(mixSalt, this.publicKey); //解密盐值
        String signNameTarget = getSignName(deSourceData, saltData);

        if (!StringUtils.equals(signNameSource, signNameTarget)) {
            throw new Exception("数据验签失败");
        }
        return result;
    }

    /**
     * 数据解析
     *
     * @param strData 待解析数据
     * @return
     */
    private Map<String, String> splitStr(String strData) throws Exception {
        try {
            strData = URLDecoder.decode(strData, AuthCheckConstant.CODE_SET);
            Map<String, String> valuePairs = new LinkedHashMap<String, String>();
            String[] pairs = strData.split("&");
            for (String pair : pairs) {
                int idx = pair.indexOf("=");
                valuePairs.put(
                        URLDecoder.decode(pair.substring(0, idx), AuthCheckConstant.CODE_SET),
                        URLDecoder.decode(pair.substring(idx + 1), AuthCheckConstant.CODE_SET));
            }
            return valuePairs;
        } catch (Exception e) {
            throw new Exception("解析数据发生错误！待解析数据为：sourcedata=" + strData, e);
        }
    }

    /***
     * 拼接参数,签名,加密后的盐值
     *
     * @param text           明文
     * @param signNameSource 签名
     * @param mixSalt        加密后的盐值
     * @return
     */
    private String buildUrlParams(String text, String signNameSource, String mixSalt) {
        return text + "&" + AuthCheckConstant.SIGN_NAME + "=" + signNameSource + "&" +
                AuthCheckConstant.MIX_SALT + "=" + mixSalt;
    }

    /***
     * 拼接参数 BIZ_TYPE 不对外开放
     *
     * @param dataSource
     * @param bizNo
     * @param extraData
     * @return
     */
    private String buildParams(String dataSource, String bizNo, String extraData) {
        StringBuilder builder = new StringBuilder(128); // 预估字符串长度,降低动态扩容几率
        builder.append(AuthCheckConstant.ORG_CODE).append("=").append(this.orgCode).append("&").
                append(AuthCheckConstant.DATA_SOURCE).append("=").append(dataSource).append("&").
                append(AuthCheckConstant.BIZ_TYPE).append("=").append("CreditLoan").append("&").
                append(AuthCheckConstant.BIZ_NO).append("=").append(bizNo).append("&");
        if (StringUtils.isNotBlank(extraData)) {
            builder.append(AuthCheckConstant.EXTRA_DATA).append("=").append(extraData);
        }
        return builder.toString();
    }


    /**
     * 获取盐值
     *
     * @return 如: 5686.548946563618
     */
    private String getSaltData() {
        return String.valueOf(Math.random() * 10000);
    }

    /***
     * 获取bizNo 规则: C + yyyyMMdd(8位) + 时间戳(13位) + 10位随机数
     *
     * @return
     */
    private String getBizNo() {
        StringBuilder bizNo = new StringBuilder(32);
        bizNo.append("C")
                .append(DateExtUtils.Formatter.FORMATTER_YYYYMMDD.format(new Date()))
                .append(System.currentTimeMillis())
                .append(RandomStringUtil.randomNumeric(10));
        return bizNo.toString();
    }

    public String getOrgCode() {
        return orgCode;
    }

    public String getPrivateKey() {
        return privateKey;
    }

    public String getPublicKey() {
        return publicKey;
    }

}
